﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Space
{
    public class Ship : Modelled, ITarget
    {
        protected const int HyperdriveMultiplier = 10;

        public string Action { get; private set; }
        public Positioned ActionTarget { get; private set; }
        public void SetAction(string action, Positioned target)
        {
            Action = action;
            ActionTarget = target;
        }

        const float DistanceThreshold = 0.5f;
        const float DistanceThresholdSquared = DistanceThreshold * DistanceThreshold;
        protected float captureDistance = 2.2f;

        protected float pushCooldown = 0;

        public Player Player { get; set; }

        public float Speed { get; set; }

        public float Range { get; set; }

        public Ship Target { get; set; }

        public float HP { get; set; }

        public virtual bool Alive { get; set; }

        public float FireInterval { get; set; }
        protected float fire = 0;

        public virtual float Angle { get; set; }

        public int CreditCost { get; set; }
        public int Value { get { return CreditCost; } }

        public bool NoCollision { get; set; }

        protected Game game;
        public Game Game { get { return game; } }

        public const float FUZZ = 1e-2f;

        public PositionSized Destination { get; set; }

        public virtual bool Visible { get { return true; } }

        public bool NoExplode { get; set; }

        public Ship(Game game)
        {
            this.game = game;
            Alive = true;
        }

        public virtual void Update(float time)
        {
        }



        public void Push<T>(List<T> others, float time) where T : Ship
        {
            foreach (Ship s in others.Where(s => s != this && !s.NoCollision))
            {
                float distanceSquared = (s.Position - Position).LengthSquared();

                if (distanceSquared < DistanceThresholdSquared * 9)
                {
                    if (distanceSquared < DistanceThresholdSquared)
                    {
                        if (distanceSquared == 0)
                        {
                            float angle = (float)Game1.Rand.NextDouble() * MathHelper.TwoPi;

                            Position += new Vector3((float)Math.Cos(angle) * DistanceThreshold, 0, (float)Math.Sin(angle) * DistanceThreshold);
                            s.Position -= new Vector3((float)Math.Cos(angle) * DistanceThreshold, 0, (float)Math.Sin(angle) * DistanceThreshold);
                        }
                        else
                        {
                            Vector3 dir = s.Position - Position;
                            dir.Normalize();
                            dir.Y = 0;
                            Position += -dir * DistanceThreshold / 2;
                            s.Position += dir * DistanceThreshold / 2;
                        }
                    }
                    else
                    {
                        Vector3 dir = s.Position - Position;
                        dir.Normalize();
                        dir.Y = 0;
                        Position += -dir * Speed * 0.5f * time;
                        s.Position += dir * s.Speed * 0.5f * time;
                    }
                    pushCooldown = 0.01f;
                    s.pushCooldown = 0.01f;
                }
            }
        }

        public void PickTarget(IEnumerable<Ship> ships)
        {
            float RangeSquare = Range * Range;
            if (Target == null || !Target.Alive || (Target.Position - Position).LengthSquared() > RangeSquare)
            {
                Target = null;
                Target = (from ship in ships
                          where ship.Player != Player && (ship.Position - Position).LengthSquared() <= RangeSquare
                          orderby Game1.Rand.Next()
                          select ship).FirstOrDefault();
            }
        }

        public void ConquerPlanet(List<Planet> planets)
        {
            //if (Target == null || !Target.Alive)
            {
                foreach (Planet p in planets)
                {
                    if (p.Player != Player)
                    {
                        if ((p.Position - Position).Length() < captureDistance)
                        {
                            float damage = Math.Min(HP, p.Population);
                            HP -= damage;
                            if(HP <= 0)
                                Alive = false;

                            p.Population -= damage;
                            if (p.Population <= 0)
                            {
                                p.Population = 0;
                                p.Player.RemovePlanet(p);
                                p.Player = Player;
                                p.Player.AddPlanet(p);
                            }
                            break;
                        }
                    }
                }
            }
        }

        public void Fire()
        {
            if((Target.Position - Position).Length() > Range || !Target.Alive) Target = null;

            if (Target != null)
            {
                Missile m = new Missile(this)
                {
                    Position = this.Position,
                    Speed = 0.15f,
                    Damage=1,
                    Target = this.Target
                };
                game.Services.GetService<IData>().AddWeapon(m);
            }
        }

        public void CopyFrom(Ship ship)
        {
            Speed = ship.Speed;
            Range = ship.Range;
            HP = ship.HP;
            FireInterval = ship.FireInterval;

            Model = ship.Model;
            Texture = ship.Texture;
        }

        public void Draw(ref Matrix proj, ref Matrix view, ref Vector3 lightDir)
        {
            if (Visible)
            {
                foreach (ModelMesh m in Model.Meshes)
                {
                    foreach (Effect effect in m.Effects)
                    {
                        effect.CurrentTechnique = effect.Techniques["Technique1"];
                        effect.Parameters["World"].SetValue(Matrix.CreateScale(Scale) * Matrix.CreateRotationY(MathHelper.PiOver2 - Angle) * Matrix.CreateTranslation(Position));
                        effect.Parameters["Projection"].SetValue(proj);
                        effect.Parameters["View"].SetValue(view);
                        //effect.Parameters["Texture"].SetValue(Texture);
                        effect.Parameters["EmissiveColor"].SetValue(Color.Black.ToVector3());
                        effect.Parameters["DiffuseColor"].SetValue(Player.Color.ToVector3());
                        effect.Parameters["Light1Direction"].SetValue(lightDir);
                    }
                    m.Draw();
                }
            }
        }

        public void TakeDamage(float damage)
        {
            HP -= damage;
            if (HP < 0) Alive = false;
        }

        public static Ship CreateShip(Game game, string type)
        {
            Ship ship;
            switch (type)
            {
                case "colony":
                    ship = new CapitalShip(game)
                    {
                        HP = 1,
                        Speed = 6.0f,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 1000,
                        Colony = true,

                        Model = game.Content.Load<Model>("models/corvette"),

                        Class = ShipClass.Colony,

                        Radius = 1,

                        FighterNum = 0,

                        BuildingTime = 1,
                    };
                    break;
                case "corvette":
                    ship = new CapitalShip(game)
                    {
                        HP = 1,
                        Speed = 18.0f,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 400,

                        Model = game.Content.Load<Model>("models/corvette"),
                        Class = ShipClass.Corvette,
                        Radius = 1,
                        FighterNum = 0,
                        BuildingTime = 1,
                    };
                    CapitalShip c = ship as CapitalShip;
                    c.AddGun(new MissileGun(ship)
                    {
                        FireInterval = 0.2f,
                        Range = 20,
                        Missile = new Missile(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            Speed = 0.15f,
                        }
                    });
                    break;
                case "destroyer":
                    ship = new CapitalShip(game)
                    {
                        HP = 2,
                        Speed = 12.0f,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 800,

                        Model = game.Content.Load<Model>("models/destroyer"),
                        Class = ShipClass.Destroyer,
                        Radius = 1,
                        FighterNum = 2,
                        BuildingTime = 1,
                    };
                    c = ship as CapitalShip;
                    c.AddGun(new MissileGun(ship)
                    {
                        FireInterval = 0.2f,
                        Range = 20,
                        Missile = new Missile(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            Speed = 0.15f,
                        }
                    });
                    c.AddGun(new LaserGun(ship)
                    {
                        FireInterval = 3.5f,
                        Range = 10,
                        Laser = new Laser(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            MaxThickness = 0.1f,
                            LifeTime = 3
                        }
                    });

                    break;
                case "cruiser":
                    ship = new CapitalShip(game)
                    {
                        HP = 2,
                        Speed = 12.0f,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 800,

                        Model = game.Content.Load<Model>("models/cruiser"),
                        Class = ShipClass.Cruiser,
                        Radius = 1,
                        FighterNum = 5,
                        BuildingTime = 1,
                    };
                                        c = ship as CapitalShip;
                    c.AddGun(new MissileGun(ship)
                    {
                        FireInterval = 0.2f,
                        Range = 20,
                        Missile = new Missile(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            Speed = 0.15f,
                        }
                    });
                    c.AddGun(new MissileGun(ship)
                    {
                        FireInterval = 0.2f,
                        Range = 20,
                        Missile = new Missile(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            Speed = 0.15f,
                        }
                    });


                    break;
                case "battleship":
                    ship = new CapitalShip(game)
                    {
                        HP = 2,
                        Speed = 6.0f,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 800,

                        Model = game.Content.Load<Model>("models/battleship"),
                        Class = ShipClass.Battleship,
                        Radius = 1,
                        FighterNum = 2,
                        BuildingTime = 1,
                    };
                    
                                        c = ship as CapitalShip;
                    c.AddGun(new MissileGun(ship)
                    {
                        FireInterval = 0.2f,
                        Range = 20,
                        Missile = new Missile(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            Speed = 0.15f,
                        }
                    });
                    c.AddGun(new LaserGun(ship)
                    {
                        FireInterval = 3.5f,
                        Range = 10,
                        Laser = new Laser(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            MaxThickness = 0.1f,
                            LifeTime = 3
                        }
                    });
                    c.AddGun(new LaserGun(ship)
                    {
                        FireInterval = 3.5f,
                        Range = 10,
                        Laser = new Laser(ship)
                        {
                            Damage = 1,
                            Ship = ship,
                            MaxThickness = 0.1f,
                            LifeTime = 3
                        }
                    });

break;
                case "carrier":
                    ship = new CapitalShip(game)
                    {
                        HP = 2,
                        Speed = 6.0f,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 800,

                        Model = game.Content.Load<Model>("models/carrier"),
                        Class = ShipClass.Carrier,
                        Radius = 1,
                        FighterNum = 20,
                        BuildingTime = 1,
                    }; break;
                case "pirate":
                    ship = new PirateShip(game)
                    {
                        HP = 1,
                        Speed = 18.3f,
                        Range = 10.0f,
                        FireInterval = 1,
                        CreditCost = 400,
                        Acceleration = 10,
                        TurnSpeed = 2.0f,

                        Model = game.Content.Load<Model>("models/corvette"),
                        Radius = 1,
                    };


                    break;
                default:
                    throw new Exception("unknown ship type");
            }
            return ship;
        }
    }
}
